package com.lx.composetest.glpaint
/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Color
import android.graphics.Paint
import android.graphics.PixelFormat
import android.opengl.GLES20
import android.opengl.Matrix
import android.os.Build
import android.util.Log
import android.view.InputDevice
import android.view.MotionEvent
import android.view.SurfaceHolder
import android.view.SurfaceView
import androidx.annotation.RequiresApi
import androidx.graphics.lowlatency.BufferInfo
import androidx.graphics.lowlatency.GLFrontBufferedRenderer
import androidx.graphics.opengl.egl.EGLManager
import androidx.input.motionprediction.MotionEventPredictor
import com.chillingvan.canvasgl.glcanvas.GLES20Canvas
import com.chillingvan.canvasgl.glcanvas.GLPaint
import com.chillingvan.canvasgl.shapeFilter.BasicDrawShapeFilter
import com.lx.composetest.ink.InkRecorder
import com.lx.composetest.lowlatency.LineRenderer
import com.lx.composetest.lowlatency.loadShader
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import java.util.concurrent.atomic.AtomicInteger


@SuppressLint("ClickableViewAccessibility")
@RequiresApi(Build.VERSION_CODES.Q)
class GLPainterSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {
    private var mFrontBufferRenderer: GLFrontBufferedRenderer<FloatArray>? = null

    /**
     * LineRenderer to be created on the GL Thread
     */
    private var mWidth: Int = 0
    private var mHeight: Int = 0
    private var mPreviousX: Float = 0f
    private var mPreviousY: Float = 0f
    private var mCurrentX: Float = 0f
    private var mCurrentY: Float = 0f
    private val LINE = 0f
    private val PREDICT = 1f
    private val motionPredictor = MotionEventPredictor.newInstance(this)
    private lateinit var canvas: GLES20Canvas
    private val defaultShapeFilter = BasicDrawShapeFilter()
    private val paint = GLPaint().apply {
        this.color = Color.RED
        this.lineWidth = 10f
        this.style = Paint.Style.STROKE
    }

    private val vertexShaderCode = "attribute vec4 vPosition;" +
            "attribute vec2 aTexCoord;" +
            "varying vec2 vTexCoord;" +
            "void main() {" +
            "  gl_Position = vPosition;" +
            "  vTexCoord = vec2(1.0 - aTexCoord.x, aTexCoord.y);" +
            "}"


    private val mCallbacks = object : GLFrontBufferedRenderer.Callback<FloatArray> {
        private val mMVPMatrix = FloatArray(16)
        private val mProjection = FloatArray(16)
        private val mGlProgram = GLES20.glCreateProgram()
        private val mMvpMatrixHandle = GLES20.glGetUniformLocation(mGlProgram, "uMVPMatrix")
        private val mvpMatrix = FloatArray(16)
        override fun onDrawFrontBufferedLayer(
            eglManager: EGLManager,
            bufferWidth: Int,
            bufferHeight: Int,
            bufferInfo: BufferInfo,
            transform: FloatArray,
            param: FloatArray
        ) {
            if (!this@GLPainterSurfaceView::canvas.isInitialized) {
                Log.i("TAG", "onDrawFrontBufferedLayer: ")
                canvas = GLES20Canvas()
                canvas.setSize(bufferWidth, bufferHeight)
                canvas.clearBuffer(floatArrayOf(1f, 1f, 1f, 1f))
//                initialize()
            }
            GLES20.glViewport(0, 0, bufferWidth, bufferHeight)
//            Matrix.orthoM(
//                mMVPMatrix,
//                0,
//                0f,
//                bufferWidth.toFloat(),
//                0f,
//                bufferHeight.toFloat(),
//                -1f,
//                1f
//            )
//            Matrix.multiplyMM(mProjection, 0, mMVPMatrix, 0, transform, 0)
            canvas.save()
            canvas.multiplyMatrix(transform, 0)

            drawSingleLine(param)
            canvas.restore()
        }

        override fun onDrawMultiBufferedLayer(
            eglManager: EGLManager,
            bufferWidth: Int,
            bufferHeight: Int,
            bufferInfo: BufferInfo,
            transform: FloatArray,
            params: Collection<FloatArray>
        ) {
            canvas.clearBuffer(floatArrayOf(1f, 1f, 1f, 1f))

        }
    }
    fun flipHorizontally(matrix: FloatArray) {
        val n = 4 // 矩阵的维度
        for (i in 0 until n) {
            for (j in 0 until n / 2) {
                val leftIndex = i * n + j
                val rightIndex = i * n + (n - 1 - j)
                val temp = matrix[leftIndex]
                matrix[leftIndex] = matrix[rightIndex]
                matrix[rightIndex] = temp
            }
        }
    }


    private fun drawSingleLine(param: FloatArray) {
//        canvas.drawLine(param[0], param[1], param[2], param[3], paint, defaultShapeFilter)

        canvas.drawCircle(param[0], param[1], paint.lineWidth / 2f, paint, defaultShapeFilter)
        canvas.drawCircle(param[2], param[3], paint.lineWidth / 2f, paint, defaultShapeFilter)
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        if (this::canvas.isInitialized) {
            canvas.setSize(width, height)
        }
    }

    val renderCount = AtomicInteger(0)

    init {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            requestUnbufferedDispatch(InputDevice.SOURCE_CLASS_POINTER)
        }
        setOnTouchListener { _, event ->
            motionPredictor.record(event)
            InkRecorder.addNewTouchEvent(event)
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    this.requestUnbufferedDispatch(event)
                    mCurrentX = event.x
                    mCurrentY = event.y
                    renderCount.set(0)
                }

                MotionEvent.ACTION_MOVE -> {
                    mPreviousX = mCurrentX
                    mPreviousY = mCurrentY
                    mCurrentX = event.x
                    mCurrentY = event.y
                    val line = FloatArray(5).apply {
                        this[0] = mPreviousX
                        this[1] = mPreviousY
                        this[2] = mCurrentX
                        this[3] = mCurrentY
                        this[4] = LINE
                    }
                    renderCount.incrementAndGet()
                    mFrontBufferRenderer?.renderFrontBufferedLayer(line)
                    Log.i("InkSurfaceView", "orientation :${event.orientation}")

//                    val motionEventPredicted = motionPredictor.predict()
//                    //处理预测点位
//                    if (motionEventPredicted != null) {
//                        //仅仅使用一半的预测
//                        val predictX = mCurrentX + (motionEventPredicted.x - mCurrentX) / 2f
//                        val predictY =  mCurrentY + (motionEventPredicted.y - mCurrentY) / 2f
//                        //预测点位标识：PREDICT
//                        val predictedSegment = floatArrayOf(
//                            mCurrentX, mCurrentY,
//                            predictX,predictY, PREDICT
//                        )
//                        mFrontBufferRenderer?.renderFrontBufferedLayer(predictedSegment)
//                    }
                }

                MotionEvent.ACTION_CANCEL -> {
                    mFrontBufferRenderer?.commit()
                }

                MotionEvent.ACTION_UP -> {
                    mFrontBufferRenderer?.commit()
                }
            }
            true
        }
    }


    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        mWidth = right - left
        mHeight = bottom - top
    }

    //    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
//        super.onSizeChanged(w, h, oldw, oldh)
//        mWidth = w
//        mHeight = h
//    }
    override fun onDetachedFromWindow() {
        mFrontBufferRenderer?.release(true) {
        }
        super.onDetachedFromWindow()
    }

    private companion object {
        private const val LINE_WIDTH = 10f
    }

    init {
        setZOrderMediaOverlay(true)
        holder.setFormat(PixelFormat.TRANSLUCENT)
        holder.addCallback(this)

    }


    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        mFrontBufferRenderer = GLFrontBufferedRenderer(this, mCallbacks)
    }

    override fun surfaceCreated(holder: SurfaceHolder) {
        Log.i("TAG", "surfaceCreated: ")
//        initGL()
//        canvas = CanvasGL()

//
//         canvas = CanvasGL()

    }

    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
//        canvas.setSize(width, height);
    }

    override fun surfaceDestroyed(holder: SurfaceHolder) {

    }

    private var mVertexShader: Int = -1
    private var mFragmentShader: Int = -1
    private var mGlProgram: Int = -1
    private var mPositionHandle: Int = -1
    private var mMvpMatrixHandle: Int = -1
    private var mColorHandle: Int = -1
    private val mColorArray = FloatArray(4)
    private var mVertexBuffer: FloatBuffer? = null
    private val mLineCoords = FloatArray(6)

    fun initialize() {
        mVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, LineRenderer.VertexShaderCode)
        mFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, LineRenderer.FragmentShaderCode)
        mGlProgram = GLES20.glCreateProgram()
        GLES20.glLinkProgram(mGlProgram)
        val bb: ByteBuffer =
            ByteBuffer.allocateDirect( // (number of coordinate values * 4 bytes per float)
                LineRenderer.LineCoordsSize * 4
            )
        // use the device hardware's native byte order
        bb.order(ByteOrder.nativeOrder())
        // create a floating point buffer from the ByteBuffer
        mVertexBuffer = bb.asFloatBuffer().apply {
            put(mLineCoords)
            position(0)
        }
        mPositionHandle = GLES20.glGetAttribLocation(mGlProgram, LineRenderer.vPosition)
        mMvpMatrixHandle = GLES20.glGetUniformLocation(mGlProgram, LineRenderer.uMVPMatrix)
        mColorHandle = GLES20.glGetUniformLocation(mGlProgram, LineRenderer.vColor)
    }


}